{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 获取特征矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import scipy\n",
    "\n",
    "def get_feature_dict(filename):\n",
    "    \"\"\"将ninapro_feature的MAT文件加载为字典\n",
    "\n",
    "    Args:\n",
    "        path: mat文件路径\n",
    "        filename: mat文件名\n",
    "\n",
    "    Returns:\n",
    "        数据集字典\n",
    "        [feat_set, featStim, featRep]\n",
    "    \"\"\"\n",
    "    # 读取MAT文件\n",
    "    print('load file: ' + filename + '...', end= '', flush=True)\n",
    "    dict_feature=scipy.io.loadmat(filename)\n",
    "    if (dict_feature != ()):\n",
    "        #print(ninapro_data.keys())\n",
    "        print('[ok]:%d'%(len(dict_feature['featStim'])), flush=True)\n",
    "    # 返回字典\n",
    "    return dict_feature\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "feature_dict = get_feature_dict(\"../feature/feature_S1s20.mat\")\n",
    "index = []\n",
    "for i in range(len(feature_dict['featStim'])):\n",
    "    if feature_dict['featStim'][i]!=0:\n",
    "        index.append(i)\n",
    "emg_feature = feature_dict['feat_set'][index,:,:]\n",
    "labels = feature_dict['featStim'][index,:]\n",
    "print(emg_feature.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loading file: ../feature/feature_S1.matload file: ../feature/feature_S1.mat...[ok]:206751\n",
      "[ok] 206751\n",
      "loading file: ../feature/feature_S2.matload file: ../feature/feature_S2.mat...[ok]:206659\n",
      "[ok] 206659\n",
      "loading file: ../feature/feature_S3.matload file: ../feature/feature_S3.mat...[ok]:207104\n",
      "[ok] 207104\n",
      "loading file: ../feature/feature_S4.matload file: ../feature/feature_S4.mat...[ok]:206841\n",
      "[ok] 206841\n",
      "(403854, 12, 10)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "import numpy as np\n",
    "# 多人合并训练\n",
    "def merge_multisubject(b,e):\n",
    "    emg_feature = None\n",
    "    labels = None\n",
    "    for i in range(b,e+1):\n",
    "        print(\"loading file:\",\"../feature/feature_S{0}.mat\".format(i),end='',flush=True)\n",
    "        feature_dict = get_feature_dict(\"../feature/feature_S{0}.mat\".format(i))\n",
    "        print('[ok]',len(feature_dict['featStim']),flush=True)\n",
    "        index = []\n",
    "        for i in range(len(feature_dict['featStim'])):\n",
    "            if feature_dict['featStim'][i]!=0:\n",
    "                index.append(i)\n",
    "        if(emg_feature is None):\n",
    "            emg_feature = feature_dict['feat_set'][index,:,:]\n",
    "            labels = feature_dict['featStim'][index,:]\n",
    "        else:\n",
    "            emg_feature = np.vstack((emg_feature,feature_dict['feat_set'][index,:,:])) \n",
    "            labels = np.vstack((labels,feature_dict['featStim'][index,:]))\n",
    "    return emg_feature,labels\n",
    "emg_feature,labels = merge_multisubject(1,4)\n",
    "print(emg_feature.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x223287a63a0>]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "plt.plot(labels)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.autograd import Variable\n",
    "from torchsummary import summary\n",
    "import torch.nn.functional as F\n",
    "\n",
    "\n",
    "class GengNet(nn.Module):\n",
    "    def __init__(self, class_num=None, base_features=64, window_length=256, input_channels=6):\n",
    "        super(GengNet, self).__init__()\n",
    "        self.class_num = class_num\n",
    "        self.conv1 = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=1,  # for EMG images, the channels is 1. not the signal channels: input_channels\n",
    "                      out_channels=base_features,\n",
    "                      kernel_size=3, stride=1, padding=1),\n",
    "            nn.BatchNorm2d(base_features),\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        self.conv2 = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=base_features,\n",
    "                      out_channels=base_features,\n",
    "                      kernel_size=3, stride=1, padding=1),\n",
    "            nn.BatchNorm2d(base_features),\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        self.conv3 = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=base_features,\n",
    "                      out_channels=base_features,\n",
    "                      kernel_size=1, stride=1),\n",
    "            nn.BatchNorm2d(base_features),\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        self.conv4 = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=base_features,\n",
    "                      out_channels=base_features,\n",
    "                      kernel_size=1, stride=1),\n",
    "            nn.BatchNorm2d(base_features),\n",
    "            nn.ReLU(),\n",
    "            nn.Dropout(p=0.5),\n",
    "        )\n",
    "\n",
    "        self.fcn1 = nn.Sequential(\n",
    "            nn.Linear(base_features * window_length * input_channels, 512),\n",
    "            nn.ReLU(),\n",
    "            nn.Dropout(p=0.5),\n",
    "            nn.Linear(512, 128),\n",
    "            nn.ReLU(),\n",
    "            nn.Dropout(p=0.5)\n",
    "        )\n",
    "        self.fcn2 = nn.Linear(128, self.class_num)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.conv1(x)\n",
    "        x = self.conv2(x)\n",
    "        x = self.conv3(x)\n",
    "        x = self.conv4(x)\n",
    "\n",
    "        x = self.fcn1(x.view(x.size(0), -1))\n",
    "        x = self.fcn2(x)\n",
    "        x = F.softmax(x, dim=1)\n",
    "        return x\n",
    "        \n",
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "print(\"The model will be running on\", device, \"device\")\n",
    "model_test = GengNet(class_num=40,base_features=16,window_length=10,input_channels=12).to(device)\n",
    "summary(model_test,(1,12,10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "def testAccuracy(device, model, test_loader):\n",
    "    \n",
    "    model.eval()\n",
    "    accuracy = 0.0\n",
    "    total = 0.0\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        for data in test_loader:\n",
    "            emg_data, labels = data\n",
    "            emg_data = Variable(emg_data.to(device))    # torch.Size([64, 1, 200, 12])\n",
    "            labels = Variable(labels.to(device))        # torch.Size([64])\n",
    "            # run the model on the test set to predict labels\n",
    "            outputs = model(emg_data)\n",
    "            # the label with the highest energy will be our prediction\n",
    "            _, predicted = torch.max(outputs.data, 1)\n",
    "            total += labels.size(0)\n",
    "            accuracy += (predicted == labels).sum().item()\n",
    "    \n",
    "    # compute the accuracy over all test images\n",
    "    accuracy = (100 * accuracy / total)\n",
    "    return(accuracy)\n",
    "\n",
    "def train(device, num_epochs, train_loader, test_loader):\n",
    "    best_accuracy = 0\n",
    "    model = models.resnet18(num_classes=40)\n",
    "    model.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)\n",
    "    model = model.to(device)\n",
    "    loss_func = torch.nn.CrossEntropyLoss()\n",
    "    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "    \n",
    "    train_accs = []\n",
    "    train_loss = []\n",
    "    test_accs = []\n",
    "\n",
    "    for epoch in range(num_epochs):\n",
    "        running_loss = 0.0\n",
    "        for i,(inputs, labels) in enumerate(train_loader,0):#0是下标起始位置默认为0\n",
    "            # data 的格式[[inputs, labels]]       \n",
    "    #         inputs,labels = data\n",
    "            inputs = Variable(inputs.to(device))    # torch.Size([64, 1, 200, 12])\n",
    "            labels = Variable(labels.to(device))        # torch.Size([64]) \n",
    "            #初始为0，清除上个batch的梯度信息\n",
    "            optimizer.zero_grad()         \n",
    "\n",
    "            #前向+后向+优化     \n",
    "            outputs = model(inputs)\n",
    "            loss = loss_func(outputs,labels.long())\n",
    "            loss.backward()\n",
    "            optimizer.step()\n",
    "\n",
    "            # loss 的输出，每个一百个batch输出，平均的loss\n",
    "            running_loss += loss.item()\n",
    "            if i%100 == 99:\n",
    "                print('[%d,%5d] loss :%.3f' %\n",
    "                    (epoch+1,i+1,running_loss/100),end='',flush=True)\n",
    "                running_loss = 0.0\n",
    "            train_loss.append(loss.item())\n",
    "\n",
    "            # 训练曲线的绘制 一个batch中的准确率\n",
    "            correct = 0\n",
    "            total = 0\n",
    "            _, predicted = torch.max(outputs.data, 1)\n",
    "            total = labels.size(0)# labels 的长度\n",
    "            correct = (predicted == labels).sum().item() # 预测正确的数目\n",
    "            train_accs.append(100*correct/total)\n",
    "            if i%100 == 99:\n",
    "                print(' acc=%d'%(100*correct/total))\n",
    "            \n",
    "        accuracy = testAccuracy(device, model, test_loader)\n",
    "        print('For epoch', epoch+1,'the test accuracy over the whole test set is %d %%' % (accuracy))\n",
    "        # we want to save the model if the accuracy is the best\n",
    "        if accuracy > best_accuracy:\n",
    "            best_accuracy = accuracy\n",
    "            torch.save(model.state_dict(), \"../model/best_epoch{1}_{0}.pth\".format(epoch, (int(time.time())%1000000)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "For epoch 3 the test accuracy over the whole test set is 34 %\n",
      "[4,  100] loss :3.394 acc=34\n",
      "[4,  200] loss :3.383 acc=50\n",
      "[4,  300] loss :3.314 acc=43\n",
      "[4,  400] loss :3.379 acc=28\n",
      "[4,  500] loss :3.362 acc=37\n",
      "[4,  600] loss :3.336 acc=34\n",
      "[4,  700] loss :3.366 acc=31\n",
      "[4,  800] loss :3.345 acc=40\n",
      "[4,  900] loss :3.334 acc=43\n",
      "[4, 1000] loss :3.362 acc=21\n"
     ]
    }
   ],
   "source": [
    "import torchvision.transforms as tt\n",
    "# 矩阵数据预处理\n",
    "emg_feature = np.expand_dims(emg_feature,axis=1)*1e3\n",
    "emg_feature = emg_feature.astype(np.float32)\n",
    "labels = labels.flatten() - 1\n",
    "\n",
    "print('reshaped data:',emg_feature.shape)\n",
    "\n",
    "print('get dataloader...', end='',flush=True)\n",
    "emg_feature_t = torch.tensor(emg_feature)\n",
    "labels_t = torch.tensor(labels)\n",
    "torch_dataset = torch.utils.data.TensorDataset(emg_feature_t, labels_t)\n",
    "\n",
    "# 划分数据集与训练集\n",
    "test_size = int(len(labels)*0.2)\n",
    "train_dataset,test_dataset = torch.utils.data.random_split(dataset=torch_dataset,lengths = [len(labels)-test_size,test_size])\n",
    "train_loader = torch.utils.data.DataLoader(\n",
    "    dataset=train_dataset,      # torch TensorDataset format\n",
    "    batch_size=32,      # mini batch size\n",
    "    shuffle=True,               # 要不要打乱数据 (打乱比较好)\n",
    "    num_workers=2,              # 多线程来读数据\n",
    "    drop_last = True,\n",
    "   \n",
    ")\n",
    "test_loader = torch.utils.data.DataLoader(\n",
    "    dataset=test_dataset,      # torch TensorDataset format\n",
    "    batch_size=64,      # mini batch size\n",
    "    shuffle=False,               # 要不要打乱数据 (打乱比较好)\n",
    "    num_workers=2,              # 多线程来读数据\n",
    "    drop_last = True,\n",
    ")\n",
    "print('[ok]')\n",
    "print('begin to train.....')\n",
    "# 模型训练\n",
    "train(device, 50, train_loader, test_loader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "torch_env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13 (default, Oct 19 2022, 22:38:03) [MSC v.1916 64 bit (AMD64)]"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "91eaa33755ee0e6c8927d7837736bb7bf44cb27baec87b08c7a4a0a98fc82110"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
